/*
 *  ckr_spec_SpecRunner.cpp
 *  ckrSpec
 *
 *  Created by C. Keith Ray on 2006.11.06.
 *  Copyright 2006 __MyCompanyName__. All rights reserved.
 *
 */

#include "ckr_spec_SpecRunner.h"
#include "ckr_spec_Context.h"
#include "ckr_spec_SpecListener.h"
#include <algorithm>

namespace  ckr
{

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

SpecRunner* SpecRunner::getGlobalSpecRunner()
{
	static SpecRunner* globalSpecRunner = new SpecRunner; 
	// FYI: the language-spec says the line above will 
	// be executed only the first time getGlobalSpecRunner() is called.
	
	return globalSpecRunner;
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

RegisterContextWithRunner::RegisterContextWithRunner( 
					Context*				aContext,
					SpecRunner*			aRunner )
{
	aRunner->add( aContext );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

SpecRunner::SpecRunner()
{
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

SpecRunner::~SpecRunner()
{
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

SpecRunner& SpecRunner::add( Context* aContext )
{
	myContextList.push_back( aContext );
	
	return *this;
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

unsigned int SpecRunner::numSpecs()
{
	return myContextList.size();
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void SpecRunner::runAll( SpecListener* listenerIn )
{
	for ( ContextList::iterator i = myContextList.begin(); i != myContextList.end(); ++i )
	{
		this->runOne( *i, listenerIn );
	}
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

double SpecRunner::averageDuration() const
{
	double sum = 0.0;
	int count = myContextList.size();

	for ( ContextList::const_iterator i = myContextList.begin(); 
			i != myContextList.end(); ++i )
	{
		sum += (*i)->duration();
	}
	if ( count == 0 )
	{
		count = 1;
	}
	return sum / count;
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void SpecRunner::sortSpecsFastestFirst()
{
	sort( myContextList.begin(), myContextList.end(), ContextDurationLessThan );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void SpecRunner::sortSpecsNotYetRunFirst()
{
	sort( myContextList.begin(), myContextList.end(), ContextHasBeenRunLessThan );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void SpecRunner::sortSpecsFailingFirst()
{
	sort( myContextList.begin(), myContextList.end(), ContextFailingLessThan );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void SpecRunner::runFastOnly( SpecListener* listenerIn )
{
	ContextList fastOnes = this->fastSpecs();

	for ( ContextList::iterator i = fastOnes.begin(); 
			i != fastOnes.end(); ++i )
	{
		this->runOne( *i, listenerIn );
	}
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void SpecRunner::runNewFirst( SpecListener* listenerIn )
{
	sortSpecsNotYetRunFirst();

	for ( ContextList::iterator i = myContextList.begin(); 
			i != myContextList.end(); ++i )
	{
		this->runOne( *i, listenerIn );
	}
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void SpecRunner::runFailingFirst( SpecListener* listenerIn )
{
	sortSpecsFailingFirst();

	for ( ContextList::iterator i = myContextList.begin(); 
			i != myContextList.end(); ++i )
	{
		this->runOne( *i, listenerIn );
	}
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void SpecRunner::runOne( Context* aContext, SpecListener* listenerIn )
{
	listenerIn->specStarted( aContext->name() );
	aContext->run_spec( listenerIn );
	listenerIn->specEnded( aContext->name(), not aContext->hasFailed() );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

unsigned int SpecRunner::numSpecsPassing() const
{
	unsigned int result = 0;

	for ( ContextList::const_iterator i = myContextList.begin(); i != myContextList.end(); ++i )
	{
		if ( (*i)->hasBeenRun() and not (*i)->hasFailed() )
		{
			++result;
		}
	}
	
	return result;
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

unsigned int SpecRunner::numSpecsFailing() const
{
	unsigned int result = 0;

	for ( ContextList::const_iterator i = myContextList.begin(); i != myContextList.end(); ++i )
	{
		if ( (*i)->hasBeenRun() and (*i)->hasFailed() )
		{
			++result;
		}
	}
	
	return result;
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ContextList SpecRunner::passingSpecs() const
{
	ContextList result;

	for ( ContextList::const_iterator i = myContextList.begin(); i != myContextList.end(); ++i )
	{
		if ( (*i)->hasBeenRun() and not (*i)->hasFailed() )
		{
			result.push_back( *i );
		}
	}

	sort( result.begin(), result.end(), ContextNameLessThan );
	return result;
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ContextList SpecRunner::failingSpecs() const
{
	ContextList result;

	for ( ContextList::const_iterator i = myContextList.begin(); i != myContextList.end(); ++i )
	{
		if ( (*i)->hasBeenRun() and (*i)->hasFailed() )
		{
			result.push_back( *i );
		}
	}

	sort( result.begin(), result.end(), ContextNameLessThan );
	return result;
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ContextList SpecRunner::fastSpecs() const
{
	ContextList result;

	double averageDur = this->averageDuration();

	for ( ContextList::const_iterator i = myContextList.begin(); 
			i != myContextList.end(); ++i )
	{
		if ( (*i)->duration() <= averageDur )
		{
			result.push_back( *i );
		}
	}
	sort( result.begin(), result.end(), ContextDurationLessThan );

	return result;
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ContextList SpecRunner::newSpecs() const
{
	ContextList result;

	for ( ContextList::const_iterator i = myContextList.begin(); 
			i != myContextList.end(); ++i )
	{
		if ( (*i)->hasBeenRun() == false )
		{
			result.push_back( *i );
		}
	}
	sort( result.begin(), result.end(), ContextNameLessThan );

	return result;
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ContextList SpecRunner::allSpecs() const
{
	ContextList result = myContextList;
	sort( result.begin(), result.end(), ContextNameLessThan );
	return result;
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

double SpecRunner::totalSpecDuration()
{
	double result = 0.0;

	for ( ContextList::iterator i = myContextList.begin(); i != myContextList.end(); ++i )
	{
		if ( (*i)->hasBeenRun() )
		{
			result += (*i)->duration();
		}
	}
	
	return result;
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


}; // end namespace
